﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NetworkManagement;
using DataObject = LorraineSec.DatabaseLib.DataObjects;

namespace LorraineSec.MessageLib.Messages
{
    public sealed class AssetListResponse : EncryptedMessage
    {
        #region Format Constants
        /*
         * Format:
         *      2            4            4           4          1             2          x      y    
         * | # Assets | { AssetID | CompartmentID | UserID | NameLength | RFID Length | Name | RFID }
         */

        //lengths
        private const int ENC_NUMASSETS_LENGTH = 2;
        private const int SET_ASSETID_LENGTH = 4;
        private const int SET_COMPARTMENTID_LENGTH = 4;
        private const int SET_USERID_LENGTH = 4;
        private const int SET_NAMELEN_LENGTH = 1;
        private const int SET_RFIDLEN_LENGTH = 2;

        //offsets
        private const int ENC_NUMASSETS_OFFSET = 0;
        private const int SET_ASSETID_OFFSET = 0;
        private const int SET_COMPARTMENTID_OFFSET = SET_ASSETID_OFFSET + SET_ASSETID_LENGTH;
        private const int SET_USERID_OFFSET = SET_COMPARTMENTID_OFFSET + SET_COMPARTMENTID_LENGTH;
        private const int SET_NAMELEN_OFFSET = SET_USERID_OFFSET + SET_USERID_LENGTH;
        private const int SET_RFIDLEN_OFFSET = SET_NAMELEN_OFFSET + SET_NAMELEN_LENGTH;
        private const int SET_NAME_OFFSET = SET_RFIDLEN_OFFSET + SET_RFIDLEN_LENGTH;

        //known lengths
        private const int ENC_KNOWN_LENGTH = ENC_NUMASSETS_LENGTH;
        private const int SET_KNOWN_LENGTH = SET_ASSETID_LENGTH + SET_COMPARTMENTID_LENGTH + SET_USERID_LENGTH + SET_NAMELEN_LENGTH + SET_RFIDLEN_LENGTH;
        #endregion

        public struct Asset
        {
            public Asset(uint _id, string _name, uint _compID, uint _userID, string _rfid)
            {
                ID = _id;
                Name = _name;
                CompartmentID = _compID;
                UserID = _userID;
                RFID = _rfid;
            }
            public static implicit operator Asset(DataObject.Asset obj)
            {
                return new Asset(obj.ID,obj.Name,obj.CompartmentID,obj.UserID,obj.RFID);
            }

            public uint ID;
            public string Name;
            public uint CompartmentID;
            public uint UserID;
            public string RFID;
        }

        public List<Asset> Assets;

        public AssetListResponse()
        {
            init();
        }

        public AssetListResponse(byte[] bytes)
            : base(bytes)
        {
            init();
            decode();
        }

        private void init()
        {
            MessageID = (ushort)LorraineSecMessageFactory.MessageID.ASSET_LIST_RESPONSE;
            Assets = new List<Asset>();
        }

        public override void decrypt(CryptoManager crypt)
        {
           base.decrypt(crypt);
            //Decode byte array
            //Get number of assets
            ushort numAssets = Bytes.getUInt16(encData, ENC_NUMASSETS_OFFSET);
            //Set offset variable
            int offset = ENC_NUMASSETS_OFFSET + ENC_NUMASSETS_LENGTH;
            for (int i = 0; i < numAssets; ++i)
            {
                //Get Asset ID
                ushort Assetid = Bytes.getUInt16(encData, offset + SET_ASSETID_OFFSET);
                //Get Compartment ID
                uint CompartmentID = Bytes.getUInt32(encData, offset + SET_COMPARTMENTID_OFFSET);
                //Get User ID
                uint UserID = Bytes.getUInt32(encData, offset + SET_USERID_OFFSET);
                //Get length of asset name
                int nameLen = Bytes.getByte(encData, offset + SET_NAMELEN_OFFSET);
                //Get length of rfid
                int rfidLen = Bytes.getUInt16(encData, offset + SET_RFIDLEN_OFFSET);
                //Get asset name
                string name = Bytes.getString(encData, offset + SET_NAME_OFFSET, nameLen);
                //Get rfid
                string rfid = Bytes.getString(encData, offset + SET_NAME_OFFSET + nameLen, rfidLen);
                //Increment offset
                offset += SET_KNOWN_LENGTH + nameLen + rfidLen;
                //Add asset to dictionary
                Assets.Add(new Asset(Assetid, name, CompartmentID, UserID, rfid));
            }
        }

        public override void encrypt(CryptoManager crypt)
        {
            //Encode byte array
            //Precount byte array allocation size
            int strAllocSize = 0;
           
            foreach (Asset str in Assets)
            {
                strAllocSize += str.Name.Length;
            }
            
            //Allocate memory for encrypion field
            encData = new byte[ENC_KNOWN_LENGTH + Assets.Count * SET_KNOWN_LENGTH + strAllocSize];

            //Set the number of assets
            Bytes.setValue(encData, ENC_NUMASSETS_OFFSET, (ushort)Assets.Count);
            //Set offset variable
            int offset = ENC_NUMASSETS_OFFSET + ENC_NUMASSETS_LENGTH;
            
            foreach (Asset asset in Assets)
            {
                //Set Asset ID
                Bytes.setValue(encData, offset, asset.ID);
                offset += SET_ASSETID_LENGTH;
                //Set Compartment ID
                Bytes.setValue(encData, offset, asset.CompartmentID);
                offset += SET_COMPARTMENTID_LENGTH;
                //Set User ID
                Bytes.setValue(encData, offset, asset.UserID);
                offset += SET_USERID_LENGTH;
                //Set asset name length
                Bytes.setValue(encData, offset, (byte)asset.Name.Length);
                offset += SET_NAMELEN_LENGTH;
                //Set asset rfid length
                Bytes.setValue(encData, offset, (ushort)asset.RFID.Length);
                offset += SET_RFIDLEN_LENGTH;
                //Set asset name
                Bytes.setValue(encData, offset, asset.Name);
                offset += asset.Name.Length;
                //Set asset rfid
                Bytes.setValue(encData, offset, asset.RFID);
                offset += asset.RFID.Length;
            }
            
            base.encrypt(crypt);
        }
    }
}
